package io.tim.mvc_f2.util.test01;

import static java.net.URLDecoder.decode;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * 用于获取指定包名下的所有类名。<br/>
 * 并可设置是否遍历该包名下的子包的类名。<br/>
 * 并可通过 Annotation 进行过滤，避免内部类的干扰。<br/>
 * 
 * @author zhangnan
 * @since 0.0.1
 *
 */
public class ClassUtil {
	
	public static void main(String []args){  
        // 标识是否要遍历该包路径下子包的类名  
//      boolean recursive = false;  
        boolean recursive = true;  
        // 指定的包名  
      String pkg = "io.tim";  
//        String pkg = "java.lang";  
        List list = null;  
//      list = getClassList(pkg, recursive, null);  
        // 增加 author.class的过滤项，即可只选出ClassTestDemo  
        list = getClassList(pkg, recursive, null);  
          
        for(int i = 0;i < list.size(); i ++){  
            System.out.println(i +":"+list.get(i));  
        }  
    }  

	private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtil.class);

	public static List<Class<?>> getClassList(String pkgName, boolean isRecursive,
			Class<? extends Annotation> annotation) {
		List<Class<?>> clazzList = new ArrayList<Class<?>>();
		ClassLoader loader = Thread.currentThread().getContextClassLoader();

		try {
			// 按文件的形式查找
			String strFile = pkgName.replaceAll("\\.", "/");
			Enumeration<URL> urls = loader.getResources(strFile);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				if (url != null) {
					String protocol = url.getProtocol();
					String pkgPath = decode(url.getPath(), "utf-8");
					System.out.println("url protocol:" + protocol + ", package path:" + pkgPath);
					if ("file".equals(protocol)) {
						System.out.println("enter file protocol");
						// 本地自己可见的代码
						findClassName(clazzList, pkgName, pkgPath, isRecursive, annotation);

					} else if ("jar".equals(protocol)) {
						System.out.println("enter jar protocol");
						// 引用第三方 jar 的代码
						findClassName(clazzList, pkgName, url, isRecursive, annotation);
					}
				}
			}

		} catch (Exception e) {
			LOGGER.error("Get class list failure ==== ", e);
			throw new RuntimeException(e);
		}
		return clazzList;
	}

	/**
	 * 本地可见的类文件处理，找到类文件，得到类名，及完整的类路径，并将它加入到列表中
	 * 
	 * @param clazzList
	 * @param pkgName
	 * @param pkgPath
	 * @param isRecursive
	 * @param annotation
	 */
	public static void findClassName(List<Class<?>> clazzList, String pkgName, String pkgPath, boolean isRecursive,
			Class<? extends Annotation> annotation) {
		if (clazzList == null) {
			return;
		}
		// 找出 .class 文件及子包
		File[] files = filterClassFiles(pkgPath);
		System.out.println("files: " + ((files == null) ? "null" : "length=" + files.length));
		if (files != null) {
			for (File file : files) {
				String fileName = file.getName();
				if (file.isFile()) {
					// 针对 .class 文件处理
					String clazzName = getClassName(pkgName, fileName);
					addClassName(clazzList, clazzName, annotation);
				} else {
					// 针对子类包的递归
					if (isRecursive) {
						String subPkgName = pkgName + "." + fileName;
						String subPkgPath = pkgPath + "/" + fileName;
						findClassName(clazzList, subPkgName, subPkgPath, isRecursive, annotation);
					}
				}
			}
		}
	}

	/**
	 * 第三方 jar 类库的引用 <br/>
	 * 
	 */
	public static void findClassName(List<Class<?>> clazzList, String pkgName, URL url, boolean isRecursive,
			Class<? extends Annotation> annotation) {
		try {
			JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
			JarFile jarFile = jarURLConnection.getJarFile();
			System.out.println("jar file : " + jarFile);
			Enumeration<JarEntry> jarEntries = jarFile.entries();
			while (jarEntries.hasMoreElements()) {
				JarEntry jarEntry = jarEntries.nextElement();
				// 类似：javax/servlet/Filter.class
				// 或者是指定的 pkgName javax.servlet 下的子包: javax/servlet/annotation;
				// javax/servlet/http 等
				String jarEntryName = jarEntry.getName();
				String clazzName = jarEntryName.replaceAll("\\/", ".");
				int endIndex = clazzName.lastIndexOf(".");
				String prefix = null;
				if (endIndex > 0) {
					String prefix_name = clazzName.substring(0, endIndex);
					endIndex = prefix_name.lastIndexOf(".");
					if (endIndex > 0) {
						prefix = prefix_name.substring(0, endIndex);
					}
				}
				if (prefix != null && jarEntryName.endsWith(".class")) {
					// System.out.println("prefix:" + prefix +" pkgName:" +
					// pkgName);
					if (prefix.equals(pkgName)) {
						System.out.println("jar entryName:" + jarEntryName);
						addClassName(clazzList, clazzName, annotation);
					} else if (isRecursive && prefix.startsWith(pkgName)) {
						// 遍历子包名：子类
						System.out.println("jar entryName:" + jarEntryName + " isRecursive:" + isRecursive);
						addClassName(clazzList, clazzName, annotation);
					}
				}
			}
		} catch (IOException e) {
			LOGGER.error("Get class list failure ==== ", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 只获取包路径下的 .class 文件或子包
	 * 
	 * @param pkgPath
	 * @return
	 */
	public static File[] filterClassFiles(String pkgPath) {
		if (pkgPath == null) {
			return null;
		}
		File[] files = new File(pkgPath).listFiles(new FileFilter() {

			public boolean accept(File file) {

				return (file.isFile() && file.getName().endsWith("class")) || file.isDirectory();
			}

		});
		return files;
	}

	/**
	 * 将包路径和文件名拼接起来得到类的路径，如下参数得到：io.tim.mvc_f2.someClass
	 * 
	 * @param pkgName
	 *            -- io.tim.mvc_f2
	 * 
	 * @param fileName
	 *            -- someClass.class
	 * 
	 * @return
	 */
	public static String getClassName(String pkgName, String fileName) {
		int endIndex = fileName.lastIndexOf(".");
		String clazz = null;
		if (endIndex > 0) {
			clazz = fileName.substring(0, endIndex);
		}

		String clazzName = null;
		if (clazz != null) {
			clazzName = pkgName + "." + clazz;
		}
		return clazzName;
	}

	/**
	 * 将符合条件的类加入到列表中
	 * 
	 * @param clazzList
	 * @param clazzName
	 * @param annotation
	 */
	public static void addClassName(List<Class<?>> clazzList, String clazzName,
			Class<? extends Annotation> annotation) {
		if (clazzList != null && clazzName != null) {
			Class<?> clazz = null;
			try {
				clazz = Class.forName(clazzName);
				if (clazz != null) {
					if (annotation == null) {
						clazzList.add(clazz);
						System.out.println("add class: " + clazz);
					} else if (clazz.isAnnotationPresent(annotation)) {
						clazzList.add(clazz);
						System.out.println("add annotation: " + clazz);
					}
				}
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
	}

}
